<?php
/**
 * Api para adminstrar las cuentas corrientes.
 *
 * Esta api sirve para administrar un tipo especial de cuentas corrientes.
 * Si se utiliza el parametro expiryDays entonces si no hubo transacciones en tantos expiryDays dias, se
 * cancela el saldo restante de la cuenta.
 *
 */
class AccountWS_Service_Main extends Ext_Services
{
    // SEE PACKAGE AC_CONSOLIDATE_PK
    // Errors in procedure insert_transaction
    const g_no_credit = -1;
    const g_no_data_found = -2;
    const g_invalid_number = -3;
    const g_value_error = -4;
    const g_other_error = -5;

    // Errors in function consolidate_account
    const g_fno_data_found = -6;
    const g_finvalid_number = -7;
    const g_fvalue_error = -8;
    const g_fother_error = -9;

    private $_plSqlModel;

    function __construct() {
        // @todo: usar el db adecuado !!!
        $db = Zend_Db_Table::getDefaultAdapter();
        $this->_plSqlModel = new Ext_Model_PLSqlProcedure( $db );
        $this->_plSqlModel->setPackage('ac_consolidate_pk');
    }

    /**
     * Crea una cuenta en el sistema.
     *
     * Hace la consolidacion de la cuenta y luego inserta una transaccion en la cuenta.
     * Los parametros sourceObjectId y sourceObjectCode permiten la identificacion de quien inserta la transaccion.
     * Devuelve el saldo de la cuenta.
     *
     * @param integer $userId id del usuario a validar con la credencial.
     * @param string $credential credencial del usuario.
     * @param integer $sourceObjectId id externo para identificar quien creo la cuenta.
     * @param string $sourceObjectCode codigo del sistema que esta creando la cuenta.
     * @param integer $expiryDays si pasaron tantos dias contados desde la ultima transaccion se pone el saldo a cero.
     * @return integer account id
     */
    function createAccount( $userId, $credential, $sourceObjectId, $sourceObjectCode,  $expiryDays ) {
        $this->_validateCredential($userId, $credential);
        if ( ! is_numeric( $sourceObjectId ) ) {
            throw new Ext_Exception("createAccount invalid parameter sourceObjectId not numeric");
        }
        if ( strlen( $sourceObjectCode ) > 20 ) {
            throw new Ext_Exception("createAccount invalid parameter sourceObjectCode len > 20");
        }
        if (! is_numeric( $expiryDays ) ) {
            throw new Ext_Exception("createAccount invalid parameter expiryDays not numeric");
        }
        try {
            $accountDb = new AccountWS_Model_Account();
            $account = $accountDb->getAccount( $sourceObjectId, $sourceObjectCode );
            if ( count( $account ) != 0 ) {
                throw new Ext_Exception("La cuenta ya existe");
            }
            return $this->createAccountInternal( $accountDb, $sourceObjectId, $sourceObjectCode,  $expiryDays );
        } catch ( Exception $e ) {
            throw new Ext_Exception( $e );
        }
    }

    /**
     * Devuelve el id de una cuenta o la crea si no existe.
     *
     * Devuelve el id de una cuenta o la crea si no existe. Mezcla de getAccount y createAccount
     * Los parametros sourceObjectId y sourceObjectCode permiten la identificacion de quien inserta la transaccion.
     * Devuelve el saldo de la cuenta.
     *
     * @param integer $userId id del usuario a validar con la credencial.
     * @param string $credential credencial del usuario.
     * @param integer $sourceObjectId id externo para identificar quien creo la cuenta.
     * @param string $sourceObjectCode codigo del sistema que esta creando la cuenta.
     * @param integer $expiryDays si pasaron tantos dias contados desde la ultima transaccion se pone el saldo a cero.
     * @return integer account id
     */
    function openAccount( $userId, $credential, $sourceObjectId, $sourceObjectCode,  $expiryDays ) {
    	
        $this->_validateCredential($userId, $credential);
        if ( ! is_numeric( $sourceObjectId ) ) {
            throw new Ext_Exception("createAccount invalid parameter sourceObjectId not numeric");
        }
        if ( strlen( $sourceObjectCode ) > 20 ) {
            throw new Ext_Exception("createAccount invalid parameter sourceObjectCode len > 20");
        }
        if (! is_numeric( $expiryDays ) ) {
            throw new Ext_Exception("createAccount invalid parameter expiryDays not numeric");
        }
        try {
            $accountDb = new AccountWS_Model_Account();
            $account = $accountDb->getAccount( $sourceObjectId, $sourceObjectCode );
            if ( count( $account ) != 0 ) {
                return $account->account_id;
            }
            return $this->createAccountInternal( $accountDb, $sourceObjectId, $sourceObjectCode,  $expiryDays );
        } catch ( Exception $e ) {
            throw new Ext_Exception( $e );
        }
        return $accountId;
    }

    private function createAccountInternal( $accountDb, $sourceObjectId, $sourceObjectCode,  $expiryDays ) {
        $account = $accountDb->createRow();
        $account->source_object_id = $sourceObjectId;
        $account->source_object_code = $sourceObjectCode;
        $account->locked = 'F';
        $account->creation_date = new Ext_Db_Date( Ext_Db_Date::NOW );
        $account->source_object_code = $sourceObjectCode;
        $account->expiry_days = $expiryDays;
        $accountId = $account->save();
        return $accountId;
    }


    /**
     * Obtiene el id de una cuenta en base al sourceObjectId + sourceObjectCode.
     *
     * Obtiene el id de una cuenta en base al sourceObjectId + sourceObjectCode.
     *
     * @param integer $userId id del usuario a validar con la credencial.
     * @param string $credential credencial del usuario.
     * @param integer $sourceObjectId id externo para identificar quien creo la cuenta.
     * @param string $sourceObjectCode codigo del sistema que esta creando la cuenta.
     * @return integer account id
     */
    function getAccountId( $userId, $credential, $sourceObjectId, $sourceObjectCode ) {
        $this->_validateCredential($userId, $credential);
        if ( ! is_numeric( $sourceObjectId ) ) {
            throw new Ext_Exception("getAccount invalid parameter sourceObjectId not numeric");
        }
        if ( strlen( $sourceObjectCode ) > 20 ) {
            throw new Ext_Exception("getAccount invalid parameter sourceObjectCode len > 20");
        }
        try {
            $accountDb = new AccountWS_Model_Account();
            $account = $accountDb->getAccount( $sourceObjectId, $sourceObjectCode );
            if ( count( $account ) == 0 ) {
                throw new Ext_Exception("La cuenta no existe o esta borrada");
            }
            return $account->account_id;
        } catch ( Exception $e ) {
            throw new Ext_Exception( $e );
        }
    }


    /**
     * Bloquea una cuenta.
     *
     * Bloquea una cuenta, la columna existe en la base de datos, pero no se si se utiliza.
     *
     *
     * @param integer $userId id del usuario a validar con la credencial.
     * @param string $credential credencial del usuario.
     * @param integer $accountId id de la cuenta
     * @return boolean true si tuvo exito
     */
    function lockAccount( $userId, $credential, $accountId ) {
        $this->_validateCredential($userId, $credential);
        if (! is_numeric( $accountId ) ) {
            throw new Ext_Exception("lockAccount invalid parameter value not numeric");
        }
        $accountDb = new AccountWS_Model_Account();
        $account = $accountDb->find( $accountId )->current();
        if ( empty( $account ) ) {
            return false;
        }
        try {
            $account->locked = 'T';
            $account->save();
        } catch ( Exception $e ) {
            throw new Ext_Exception( $e );
        }
        return true;
    }

    /**
     * Desbloquea una cuenta.
     *
     * Desbloquea una cuenta, la columna existe en la base de datos, pero no se si se utiliza.
     *
     * @param integer $userId id del usuario a validar con la credencial.
     * @param string $credential credencial del usuario.
     * @param integer $accountId id de la cuenta
     * @return boolean true si tuvo exito
     */
    function unlockAccount( $userId, $credential, $accountId ) {
        $this->_validateCredential($userId, $credential);
        if (! is_numeric( $accountId ) ) {
            throw new Ext_Exception("lockAccount invalid parameter value not numeric");
        }
        $accountDb = new AccountWS_Model_Account();
        $account = $accountDb->find( $accountId )->current();
        if ( empty( $account ) ) {
            return false;
        }
        try {
            $account->locked = 'F';
            $account->save();
        } catch ( Exception $e ) {
            throw new Ext_Exception( $e );
        }
        return true;
    }


    /**
     * Borra una cuenta
     *
     * Borra una cuenta se usa end_date en la tabla accounts, asi que el historico queda.
     *
     *
     * @param integer $userId id del usuario a validar con la credencial.
     * @param string $credential credencial del usuario.
     * @param integer $accountId id de la cuenta
     * @return boolean true si tuvo exito
     */
    function deleteAccount( $userId, $credential, $accountId ) {
        $this->_validateCredential($userId, $credential);
        if (! is_numeric( $accountId ) ) {
            throw new Ext_Exception("deleteAccount invalid parameter value not numeric");
        }
        $accountDb = new AccountWS_Model_Account();
        $account = $accountDb->find( $accountId )->current();
        if ( empty( $account ) ) {
            return false;
        }
        try{
            $account->end_date = new Ext_Db_Date( Ext_Db_Date::NOW );
            $account->save();
        } catch ( Exception $e ) {
            throw new Ext_Exception( $e );
        }
        return true;
    }

    /**
     * Hace la cosolidacion de una cuenta y se fija si alcanza para pagar el monto amount.
     *
     * Esta funcion hace la consolidacion de la cuenta y devuelve cuanto hay que debitar para poder pagar el monto amount.
     *
     * @param integer $userId id del usuario a validar con la credencial.
     * @param string $credential credencial del usuario.
     * @param integer $accountId id de la cuenta
     * @param double $amount
     * @return double cuanto hay que debitar para poder pagar el monto amount
     */
    function getAmountToPay( $userId, $credential, $accountId, $amount ) {
        $this->_validateCredential($userId, $credential);
        if (! is_numeric( $amount ) ) {
            throw new Ext_Exception("invalid parameter amount value not numeric");
        }
        $balance = $this->balanceAccount( $userId, $credential,$accountId );
        if ( $balance - $amount >= 0 ) {
            return 0;
        }
        return  $amount - $balance;
    }

    /**
     * Hace la cosolidacion de una cuenta.
     *
     * Esta funcion hace la consolidacion de la cuenta y devuelve el saldo.
     * Si pasaron expiryDays desde la ultima transaccion entonces se cancela el saldo restante en la cuenta.
     *
     * @param integer $userId id del usuario a validar con la credencial.
     * @param string $credential credencial del usuario.
     * @param integer $accountId id de la cuenta
     * @return integer saldo en la cuenta menor que cero si es error
     */
    function balanceAccount( $userId, $credential, $accountId ) {
        $this->_validateCredential($userId, $credential);
        if (! is_numeric( $accountId ) ) {
            throw new Ext_Exception("balanceAccount invalid parameter accountId value not numeric");
        }
        $r_saldo = str_pad( "", 21 );

        $accountDb = new AccountWS_Model_Account();
        $select = $accountDb->select()
        ->where( "end_date is null" )
        ->where( "account_id = ?", $accountId );
        $account = $accountDb->fetchRow( $select );
        if ( empty( $account ) ) {
            throw new Ext_Exception("La cuenta no existe o esta borrada");
        }

        try {
            $key = '4cc0ns0l1d4t3';
            $this->_plSqlModel->balance_account( array( "p_account_id" => $accountId,
                                                        "p_vencimiento_days" => $account->expiry_days,
                                                        "p_key" => $key,
                                                        "x_saldo" => &$r_saldo
                ));
        } catch ( Exception $e ) {
            throw new Exception( $e->__toString() . " " . $e->getMessage() );
        }
        if ( ! is_numeric( $r_saldo ) && trim( $r_saldo ) != "" ) {
            throw new Exception( "balanceAccount package return value " . Zend_Debug::dump( $r_saldo, "", false ) . " invalid" );
        }
        $retval = intval( $r_saldo );
        if ( $retval < 0 ) {
            throw new Ext_Exception( "balanceAccount error $retval in package" );
        }
        return $retval;
    }

    /**
     * Inserta una transaccion en una cuenta.
     *
     * Hace la consolidacion de la cuenta y luego inserta una transaccion en la cuenta.
     * Los parametros sourceObjectId y sourceObjectCode permiten la identificacion de quien inserta la transaccion.
     * Devuelve el saldo de la cuenta.
     *
     * @param integer $userId id del usuario a validar con la credencial.
     * @param string $credential credencial del usuario.
     * @param integer $accountId id de la cuenta
     * @param double $amount valor monetario de la transaccion.
     * @param integer $sourceObjectId id externo para identificar quien creo la transaccion.
     * @param string $sourceObjectCode codigo del sistema que esta creando la transaccion.
     * @return integer saldo en la cuenta menor que cero si es error
     */
    function insertTransaction( $userId, $credential, $accountId, $amount, $sourceObjectId, $sourceObjectCode ) {
        $this->_validateCredential($userId, $credential);
        if (! is_numeric( $accountId ) ) {
            throw new Ext_Exception("insertTransaction invalid parameter value not numeric");
        }
        if (! is_numeric( $sourceObjectId ) ) {
            throw new Ext_Exception("insertTransaction invalid parameter sourceObject not numeric");
        }
        if ( strlen($sourceObjectCode) > 20 ) {
            throw new Ext_Exception("insertTransaction invalid parameter sourceObjectCode len > 20");
        }
        $r_saldo = str_pad( "", 21 );

        $accountDb = new AccountWS_Model_Account();
        $select = $accountDb->select()
        ->where( "end_date is null" )
        ->where( "locked = ?" , 'F' )
        ->where( "account_id = ?", $accountId );
        $account = $accountDb->fetchRow( $select );
        if ( empty( $account ) ) {
            throw new Ext_Exception("La cuenta no existe, esta bloqueada o esta borrada");
        }


        try {
            $this->_plSqlModel->insert_transaction( array(  "p_account_id" => $accountId,
                                                            "p_amount" => $amount,
                                                            "p_source_object_type" => $sourceObjectCode,
                                                            "p_source_object_id" => $sourceObjectId ,
                                                            "p_vencimiento_days" => $account->expiry_days,
                                                            "x_saldo" => &$r_saldo
                ));
        } catch ( Exception $e ) {
            throw new Ext_Exception( $e );
        }
        if ( ! is_numeric( $r_saldo ) && trim( $r_saldo ) != "" ) {
            throw new Exception( "insertTransaction package return value " . Zend_Debug::dump( $r_saldo, "", false ) . " invalid" );
        }
        $retval = intval( $r_saldo );
        if ( $retval < 0 ) {
            throw new Ext_Exception( "insertTransaction error $retval in package" );
        }
        return $retval;
    }


     /**
     * Lista las transacciones de una cuenta.
     *
     * Lista las transacciones en una cuenta.
     * No hace al consolidacion por lo cual el saldo puede contener saldos vencidos.
     *
     * @param integer $userId id del usuario a validar con la credencial.
     * @param string $credential credencial del usuario.
     * @param integer $accountId id de la cuenta
     * @return mixed listado con los campos de la cuenta
     */
    function listTransactions( $userId, $credential, $accountId ) {
        $this->_validateCredential($userId, $credential);
        if (! is_numeric( $accountId ) ) {
            throw new Ext_Exception("listTransactions invalid parameter value not numeric");
        }

        $accountDb = new AccountWS_Model_AccountTrx();
        $select = $accountDb->select()
        ->where( "account_id = ?", $accountId )
        ->order( "trx_id asc" );
        $account = $accountDb->fetchAll( $select );
        return $account->toArray();
    }


}
